# -*- coding:utf-8 -*-
import os
import shutil
import time
import webbrowser
import cv2
import pyperclip
import threading
import tkinter as tk
from tkinter import *
from PIL import Image, ImageTk  # 图像控件
import numpy as np
import tkinter.filedialog as tkf
from tkinterdnd2 import *
from tkinter import ttk
import tkinter.font as tkFont
import tkinter.messagebox as tkm
from subprocess import Popen, PIPE, STDOUT, TimeoutExpired
from threading import Thread, Event
from queue import Queue, Empty
from tkinter import Text, END

clock_icon = './load_file/icon/clock.ico'
train_cmd_icon = './load_file/icon/train_cmd.ico'
mask_icon = './load_file/icon/mask_icon.ico'  # 窗口左上角图标
mask_title = './load_file/icon/mask_title.png'  # 标题右图标
bg_path = './load_file/bg/'  # 窗口大图目录
init_bg = './load_file/bg/bg_05.jpg'  # 窗口大图
run_resut_path = './run.log'  # 小窗口生成结果日志方便复制
images_temp_path = './temp_images/'  # 生成程序保存结果图
weight_file = 'runs/train/exp9/weights/best.pt'  # 默认权重
camera_source = './photo_detection/source/'  # 拍照检测原图
camera_result = './photo_detection/result/'  # 拍照检测结果图
placed_top_image = './load_file/button/placed_top.png'  # 置顶图像
cancel_top_image = './load_file/button/cancel_top.png'  # 取消置顶图像
open_sound_image = './load_file/button/open_sound.png'  # 打开声音图像
close_sound_image = './load_file/button/close_sound.png'  # 关闭声音图像

open_camera_image = './load_file/button/open_camera.png'
close_camera_image = './load_file/button/close_camera.png'
photos_detect_image = './load_file/button/photos_detect.png'
clock_image = './load_file/button/clock.png'
start_detect_image = './load_file/button/start_detect.png'
pause_detect_image = './load_file/button/pause_detect.png'
current_result_image = './load_file/button/current_result.png'
empty_image = './load_file/button/empty.png'
reset_canvas_image = './load_file/button/reset_canvas.png'
reset_button_image = './load_file/button/reset_button.png'

select_file_image = './load_file/button/select_file.png'
start_run_image = './load_file/button/start_run.png'
select_weights_image = './load_file/button/select_weights.png'
link_detect_image = './load_file/button/link_detect.png'
reset_thres_image = './load_file/button/reset_thres.png'
update_value_image = './load_file/button/update_value.png'

confirm_image = './load_file/button/confirm.png'

clear_image = './load_file/button/clear.png'
copy_result_image = './load_file/button/copy_result.png'

train_result_image = './load_file/button/train_result.png'
train_cmd_image = './load_file/button/train_cmd.png'
detect_result_image = './load_file/button/detect_result.png'

conda_tool_image = './load_file/button/conda_tool.png'
about_image = './load_file/button/about.png'

weight_format = ['pt', 'pth']  # 权重格式
image_format = ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'dng', 'webp', 'mpo']  # 可检测图片格式
video_format = ['mov', 'avi', 'mp4', 'mpg', 'mpeg', 'm4v', 'wmv', 'mkv']  # 可检测视频格式

top = TkinterDnD.Tk()  #
top.title('口罩佩戴检测')  # 窗口标题
top.geometry('1660x900+120+60')  # 窗口大小
top.resizable(0, 0)  # 禁止缩放
top.config(bg='#F0F0F0')  # 窗口背景颜色

font_Title = ("微软雅黑 Light", 20, "bold")  # 标题字体
font_Radio = ("微软雅黑", 12, "bold")  # Radio字体
font_Scale = ("微软雅黑", 9, "bold")  # Scale字体
font_Button = ("微软雅黑", 10, "bold")  # 按钮字体
font_Label = ("微软雅黑", 9, "bold")  # 标签字体

image_width = 640  # 画布宽度
image_height = 480  # 画布高度(4:3)
image_title_width = 108  # 标题右标志图标宽度
image_title_height = 108  # 标题右标志图标高度
top.iconbitmap(mask_icon)  # 窗口左上角图标
button_column_x = [80, 435, 703, 750, 800, 900, 1050, 1200, 1350, 1500]  # 所有按钮按列的x坐标
# button_column1_y = [120, 200, 280, 360, 440, 520, 600, 680, 760] # 第1列按钮y坐标
button_column1_y = [y * 80 + 140 for y in range(9)]  # x*间距+首位置偏移 (第1列按钮y坐标)
# button_column4_y = [ 610, 660, 710, 760, 813] # 第4列按钮y坐标（选择文件、权重等）
button_column4_y = [y * 50 + 615 for y in range(4)]  # x*间距+首位置偏移 (第4列按钮y坐标)
button_column4_y.append(825)
button_right_y = [600, 690, 780]  # 右边7个按钮的y坐标


# 按钮提示文字
class ToolTip(Toplevel):
    def __init__(self, wdgt, msg=None, msgFunc=None, delay=1, follow=True):
        self.wdgt = wdgt
        self.parent = self.wdgt.master  # The parent of the ToolTip is the parent of the ToolTips widget
        Toplevel.__init__(self, self.parent, bg='black', padx=1, pady=1)  # Initalise the Toplevel
        self.withdraw()  # Hide initially
        self.overrideredirect(True)  # The ToolTip Toplevel should have no frame or title bar

        self.msgVar = StringVar()  # The msgVar will contain the text displayed by the ToolTip
        if msg == None:
            self.msgVar.set('No message provided')
        else:
            self.msgVar.set(msg)
        self.msgFunc = msgFunc
        self.delay = delay
        self.follow = follow
        self.visible = 0
        self.lastMotion = 0
        Message(self, textvariable=self.msgVar, bg='#FFFFDD',
                aspect=1000).grid()  # The test of the ToolTip is displayed in a Message widget
        self.wdgt.bind('<Enter>', self.spawn,
                       '+')  # Add bindings to the widget.  This will NOT override bindings that the widget already has
        self.wdgt.bind('<Leave>', self.hide, '+')
        self.wdgt.bind('<Motion>', self.move, '+')

    def spawn(self, event=None):
        self.visible = 1
        self.after(int(self.delay * 1000), self.show)  # The after function takes a time argument in miliseconds

    def show(self):
        if self.visible == 1 and time.time() - self.lastMotion > self.delay:
            self.visible = 2
        if self.visible == 2:
            self.deiconify()

    def move(self, event):
        self.lastMotion = time.time()
        if self.follow == False:  # If the follow flag is not set, motion within the widget will make the ToolTip dissapear
            self.withdraw()
            self.visible = 1
        self.geometry('+%i+%i' % (
            event.x_root + 10, event.y_root + 10))  # Offset the ToolTip 10x10 pixes southwest of the pointer
        try:
            self.msgVar.set(
                self.msgFunc())  # Try to call the message function.  Will not change the message if the message function is None or the message function fails
        except:
            pass
        self.after(int(self.delay * 1000), self.show)

    def hide(self, event=None):
        self.visible = 0
        self.withdraw()


# 多线关联按钮
class MyThread(threading.Thread):
    def __init__(self, func, *args):
        super().__init__()
        self.func = func
        self.args = args
        self.setDaemon(True)
        self.start()  # 在这里开始

    def run(self):
        self.func(*self.args)


# 程序结果输出到窗口中
class ProcessOutputReader(Thread):
    def __init__(self, queue, cmd, params=(),
                 group=None, name=None, daemon=True):
        super().__init__(group=group, name=name, daemon=daemon)
        self._stop_request = Event()
        self.queue = queue
        self.process = Popen((cmd,) + tuple(params),
                             stdout=PIPE,
                             stderr=STDOUT,
                             universal_newlines=True)

    def run(self):
        for line in self.process.stdout:
            if self._stop_request.is_set():
                # if stopping was requested, terminate the process and bail out
                self.process.terminate()
                break
            self.queue.put(line)  # enqueue the line for further processing
            self.write_log(line)
        try:
            # give process a chance to exit gracefully
            self.process.wait(timeout=3)
        except TimeoutExpired:
            # otherwise try to terminate it forcefully
            self.process.kill()

    def stop(self):
        # request the thread to exit gracefully during its next loop iteration
        self._stop_request.set()
        # empty the queue, so the thread will be woken up
        # if it is blocking on a full queue
        while True:
            try:
                self.queue.get(block=False)
            except Empty:
                break
            self.queue.task_done()  # acknowledge line has been processed

    def write_log(self, line):
        if not os.path.exists(run_resut_path):
            # os.system(r"touch {}".format(run_resut_path))  # 调用系统命令行来创建文件
            f = open(run_resut_path, 'a+')
            f.write(line)
            f.close()
        else:
            f = open(run_resut_path, 'a+')
            f.write(line)
            f.close()


# 自定义输出窗口
class MyConsole(Text):
    def __init__(self, parent, queue, update_interval=50, process_lines=500):
        super().__init__(parent)
        self.queue = queue
        self.update_interval = update_interval
        self.process_lines = process_lines
        self.after(self.update_interval, self.fetch_lines)

    def fetch_lines(self):
        something_inserted = False
        for _ in range(self.process_lines):
            try:
                line = self.queue.get(block=False)
            except Empty:
                break
            self.insert(END, line)
            self.queue.task_done()  # acknowledge line has been processed
            # ensure scrolling the view is at most done once per interval
            something_inserted = True
        if something_inserted:
            self.see(END)
        self.after(self.update_interval, self.fetch_lines)


# 计时类
class StopWatch(Frame):
    '''实现一个秒表部件'''
    msec = 50

    def __init__(self, parent=None, **kw):
        Frame.__init__(self, parent, kw)
        self._start = 0.0
        self._elapsedtime = 0.0
        self._running = False
        self.timestr = StringVar()
        self.makeWidgets()

    def makeWidgets(self):
        '''制作时间标签'''
        l = Label(self, textvariable=self.timestr)
        self._setTime(self._elapsedtime)
        l.pack(fill=X, expand=NO, pady=2, padx=2)

    def _update(self):
        self._elapsedtime = time.time() - self._start
        self._setTime(self._elapsedtime)
        self._timer = self.after(self.msec, self._update)

    def _setTime(self, elap):
        '''将时间格式改为 分：秒：百分秒'''
        minutes = int(elap / 60)
        seconds = int(elap - minutes * 60.0)
        hseconds = int((elap - minutes * 60.0 - seconds) * 100)
        self.timestr.set('%2d:%2d:%2d' % (minutes, seconds, hseconds))

    def Start(self):
        if not self._running:
            self._start = time.time() - self._elapsedtime
            self._update()
            self._running = True

    def Stop(self):
        '''停止秒表'''
        if self._running:
            self.after_cancel(self._timer)
            self._elapsedtime = time.time() - self._start
            self._setTime(self._elapsedtime)
            self._running = False

    def Reset(self):
        '''重设秒表'''
        self._start = time.time()
        self._elapsedtime = 0.0
        self._setTime(self._elapsedtime)

    def stopwatch(self):
        self.pack(side=TOP)
        Button(self, text='start', command=self.Start).pack(side=LEFT)
        Button(self, text='stop', command=self.Stop).pack(side=LEFT)
        Button(self, text='reset', command=self.Reset).pack(side=LEFT)
        # Button(self, text='quit', command=self.quit).pack(side=LEFT)


# 绘制画布
canvas = Canvas(top,
                bg='white',
                width=image_width,
                height=image_height)
canvas.place(x=200, y=120)

# 程序输出标题
Label(top,
      text='程\n序\n输\n出',
      font=font_Radio,
      width=2,
      height=4).place(x=1600, y=305, anchor='nw')
canvas_mask_right = Canvas(top,
                           width=image_title_width,
                           height=image_title_height)
canvas_mask_right.place(x=1155, y=8)


# 图像转化
def imgConvert(img,
               resize_flag=False,
               image_width=image_width,
               image_height=image_height):
    # 将颜色由BGR转为RGB
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = img.astype(np.uint8)
    # 转换成可以显示的图片640x480
    img = Image.fromarray(img)
    # resize图像大小为400x300
    if resize_flag:
        img = img.resize((image_width, image_height),
                         Image.ANTIALIAS)
    img = ImageTk.PhotoImage(img)
    return img


# 下拉背景图
# 初始化Combobox环境列表
xVariable = tk.StringVar()  # #创建变量，便于取值
env_com = ttk.Combobox(top, textvariable=xVariable, width=8)  # #创建下拉菜单
env_com.place(x=860, y=575)  # #将下拉菜单绑定到窗体


def init_env_com():
    bg_list = os.listdir(bg_path)
    env_com["value"] = tuple(bg_list)
    index = bg_list.index(init_bg[-9:])
    env_com.current(index)  # #设定下拉菜单的默认值为第0个


def xFunc(event):
    global bg
    bg_full_path = bg_path + env_com.get()
    bg_ = cv2.imread(bg_full_path)
    bg = imgConvert(bg_, True)
    canvas.create_image(0, 0,
                        anchor='nw',
                        image=bg,
                        tag='bg_img')
    top.update()
    top.after(1)


env_com.bind("<<ComboboxSelected>>", xFunc)  # #给下拉菜单绑定事件
# 显示环境Combobox
init_env_com()


# 点击x关闭事件
def on_close():
    # custom close options, here's one example:
    close = tkm.askokcancel("关闭", "是否关闭程序")
    if close:
        if os.path.exists('./photo_detection'):
            shutil.rmtree('./photo_detection')  # 递归删除拍照缓存文件夹
        if os.path.exists(run_resut_path):
            os.remove(run_resut_path)  # 删除日志缓存
        top.destroy()


top.protocol("WM_DELETE_WINDOW", on_close)


# 关闭打开按钮等状态功能
def close_radio(flag, camera_switch=True):
    if flag:
        cam_01_Ra.config(state='disable')
        cam_02_Ra.config(state='disable')
        cam_03_Ra.config(state='disable')
        GPU_Ra.config(state='disable')
        CPU_Ra.config(state='disable')
        conf_thres_label_scale.config(state='disable')
        iou_thres_label_scale.config(state='disable')
        thres_Btn.config(state='disable')
        reset_canvas_Btn.config(state='disable')
        reset_Btn_state_Btn.config(state='disable')
        clear_cache_Btn.config(state='disable')
        current_result_Btn.config(state='disable')
        conf_thres_Btn.config(state='disable')
        iou_thres_Btn.config(state='disable')
        sound_Btn.config(state='disable')
        env_com.config(state='disable')
        select_dir_Btn.config(state='disable')
        detect_dir_Btn.config(state='disable')
        select_file_Btn.config(state='disable')
        detect_file_Btn.config(state='disable')
        weight_Btn.config(state='disable')
        link_Btn.config(state='disable')
        delete_text_Btn.config(state='disable')
        open_train_dir_Btn.config(state='disable')
        copy_run_result_Btn.config(state='disable')
        train_cmd_Btn.config(state='disable')
        all_result_dir_Btn.config(state='disable')
        conda_tool_Btn.config(state='disable')
        about_btn.config(state='disable')
        iou_thres_Entry.config(state='disable')
        conf_thres_Entry.config(state='disable')
        if camera_switch:
            open_camera_Btn.config(state='disable')
        else:
            photo_detection_Btn.config(state='normal')
            start_detect_Btn.config(state='disable')

    else:
        cam_01_Ra.config(state='normal')
        cam_02_Ra.config(state='normal')
        cam_03_Ra.config(state='normal')
        GPU_Ra.config(state='normal')
        CPU_Ra.config(state='normal')
        conf_thres_label_scale.config(state='normal')
        iou_thres_label_scale.config(state='normal')
        thres_Btn.config(state='normal')
        reset_canvas_Btn.config(state='normal')
        reset_Btn_state_Btn.config(state='normal')
        clear_cache_Btn.config(state='normal')
        current_result_Btn.config(state='normal')
        conf_thres_Btn.config(state='normal')
        iou_thres_Btn.config(state='normal')
        sound_Btn.config(state='normal')
        env_com.config(state='normal')
        select_dir_Btn.config(state='normal')
        detect_dir_Btn.config(state='normal')
        select_file_Btn.config(state='normal')
        detect_file_Btn.config(state='normal')
        weight_Btn.config(state='normal')
        link_Btn.config(state='normal')
        delete_text_Btn.config(state='normal')
        open_train_dir_Btn.config(state='normal')
        copy_run_result_Btn.config(state='normal')
        train_cmd_Btn.config(state='normal')
        all_result_dir_Btn.config(state='normal')
        conda_tool_Btn.config(state='normal')
        about_btn.config(state='normal')
        iou_thres_Entry.config(state='normal')
        conf_thres_Entry.config(state='normal')
        if camera_switch:
            open_camera_Btn.config(state='normal')
        else:
            photo_detection_Btn.config(state='disable')
            start_detect_Btn.config(state='normal')


# 放置初始背景图
bg = cv2.imread(init_bg)
bg = imgConvert(bg, True)
canvas.create_image(0, 0,
                    anchor='nw',
                    image=bg,
                    tag='bg_img')

# 放置标题右标志图标
img_title = cv2.imread(mask_title)
img_title = imgConvert(img_title,
                       True,
                       image_title_width,
                       image_title_height)
canvas_mask_right.create_image(0, 0,
                               anchor='nw',
                               image=img_title)

################################################### 声音 ####################################################
close_sound_img = PhotoImage(file=close_sound_image)
open_sound_img = PhotoImage(file=open_sound_image)
click_sound_state = False


def placed_top_func():
    global click_sound_state
    # 第二次
    if click_sound_state:
        click_sound_state = False
        sound_Btn.config(image=close_sound_img)

    # 默认关闭，第一次点击打开
    else:
        click_sound_state = True
        sound_Btn.config(image=open_sound_img)


sound_Btn = tk.Button(top,
                      text='打开/关闭声音',
                      command=placed_top_func,
                      relief=RIDGE,
                      image=close_sound_img,
                      bd=0,
                      font=font_Button)
sound_Btn.place(x=40, y=5)
ToolTip(sound_Btn, msg=sound_Btn['text'], msgFunc=None, follow=True, delay=0)

################################################### 声音 End ################################################


################################################### conf-thres Scale ####################################################
conf_thres_input_value = tk.DoubleVar()
# conf-thres 显示框
conf_thres_Entry = tk.Entry(top,
                            textvariable=conf_thres_input_value,
                            width=4)
conf_thres_Entry.place(x=315, y=815)
conf_thres_input_value.set(0.45)
# conf-thres 标签
conf_thres_label = tk.Label(top,
                            width=8,
                            text='Conf-thres',
                            font=font_Scale)
conf_thres_label.place(x=208, y=815)


# conf-thres 显示
def print_conf_thres_selection(value):
    global conf_thres
    conf_thres = value
    conf_thres_input_value.set(value)


# conf-thres Scale
conf_thres_label_scale = tk.Scale(top,
                                  from_=0,
                                  to=1,
                                  orient=tk.HORIZONTAL,
                                  length=250,
                                  showvalue=0,
                                  tickinterval=0.2,
                                  resolution=0.01,
                                  command=print_conf_thres_selection)
conf_thres_label_scale.place(x=207, y=840)
conf_thres_label_scale.set(0.25)


# 从Entry更新到Scale
def update_conf_scale():
    conf_thres_label_scale.set(conf_thres_Entry.get())


# conf-thres 重置按钮
update_value_img = PhotoImage(file=update_value_image)
conf_thres_Btn = tk.Button(top,
                           text="更新",
                           command=update_conf_scale,
                           relief=RIDGE,
                           image=update_value_img,
                           bd=0,
                           font=("黑体", 9))
conf_thres_Btn.place(x=button_column_x[1], y=814)
ToolTip(conf_thres_Btn, msg=conf_thres_Btn['text'] + "(将输入的conf_thres更新到滑动条上)", msgFunc=None, follow=True, delay=0)
################################################### conf-thres Scale End #################################################


################################################### iou-thres Scale ####################################################
iou_thres_input_value = tk.DoubleVar()
# iou-thres 显示框
iou_thres_Entry = tk.Entry(top,
                           textvariable=iou_thres_input_value,
                           width=4)
iou_thres_Entry.place(x=582, y=815)
iou_thres_input_value.set(0.45)
# iou-thres 标签
iou_thres_label = tk.Label(top,
                           width=7,
                           text='Iou-thres',
                           font=font_Scale)
iou_thres_label.place(x=475, y=815)


# iou-thres 显示
def print_iou_thres_selection(value):
    global iou_thres
    iou_thres = value
    iou_thres_input_value.set(value)


# iou-thres Scale
iou_thres_label_scale = tk.Scale(top,
                                 from_=0,
                                 to=1,
                                 orient=tk.HORIZONTAL,
                                 length=250,
                                 showvalue=0,
                                 tickinterval=0.2,
                                 resolution=0.01,
                                 command=print_iou_thres_selection)
iou_thres_label_scale.place(x=475, y=840)
iou_thres_label_scale.set(0.45)


# 从Entry更新到Scale
def update_iou_scale():
    iou_thres_label_scale.set(iou_thres_Entry.get())


# iou-thres 重置按钮
iou_thres_Btn = tk.Button(top,
                          text="更新",
                          command=update_iou_scale,
                          image=update_value_img,
                          bd=0,
                          relief=RIDGE,
                          font=("黑体", 9))
iou_thres_Btn.place(x=button_column_x[2], y=814)
ToolTip(iou_thres_Btn, msg=iou_thres_Btn['text'] + "(将输入的iou_thres更新到滑动条上)", msgFunc=None, follow=True, delay=0)


################################################### iou-thres Scale End #################################################


################################################### 阈值重置 ####################################################
def thres_reset():
    conf_thres_label_scale.set(0.25)
    iou_thres_label_scale.set(0.45)


# 阈值重置按钮
reset_thres_img = PhotoImage(file=reset_thres_image)
thres_Btn = tk.Button(top,
                      text=" 阈 值 重 置 ",
                      command=thres_reset,
                      relief=RIDGE,
                      image=reset_thres_img,
                      bd=0,
                      # height=2,
                      font=font_Button)
thres_Btn.place(x=button_column_x[3], y=button_column4_y[4])
ToolTip(thres_Btn, msg="阈值重置(重置iou_thres和conf_thres为默认值)", msgFunc=None, follow=True, delay=0)
################################################### 阈值重置 End #################################################


################################################### 选择权重 ####################################################
weight_path = tk.StringVar()


# 拖拽权重文件
def dragWeight(event):
    global weight_file
    # 选择
    _weight_format = ""
    for weight_format_ele in weight_format:
        _weight_format += '*.' + weight_format_ele + ' '

    _weight_format = _weight_format[:-1]
    pathList = event.data.split(' ')
    if len(pathList) > 1:
        tkm.showinfo(title='提示',
                     message='权重只能选择一个，请重新选择！')
    else:
        # 判断
        path = event.data
        # 判断文件格式
        if path.split('.')[-1] in weight_format:
            if path != '':
                path_ = path.replace('/', '\\')
                # 检测
                message_Isdetect = "选择的权重文件：" + path
                if tkm.askokcancel(title='开始检测',
                                   message=message_Isdetect):
                    weight_path.set("当前权重  " + path)  # 将路径名填写框中
                    weight_file = path_
                    tkm.showinfo(title="提示",
                                 message="权重更新成功！")
                else:
                    tkm.showinfo(title="提示",
                                 message="未选择权重文件执行默认权重！")
            else:
                tkm.showinfo(title="提示",
                             message="未选择权重文件执行默认权重！")
        else:
            tkm.showinfo(title="提示",
                         message="权重文件格式须为pt或pth！")


# 选择权重按钮
def selectWeight():
    global weight_file
    # 选择
    _weight_format = ""
    for weight_format_ele in weight_format:
        _weight_format += '*.' + weight_format_ele + ' '

    _weight_format = _weight_format[:-1]
    path = tkf.askopenfilename(filetypes=
                               [("weight file", _weight_format)])
    # 判断
    if path != '':
        path_ = path.replace('/', '\\')
        # 检测
        message_Isdetect = "选择的权重文件：" + path
        if tkm.askokcancel(title='开始检测',
                           message=message_Isdetect):
            weight_path.set("当前权重  " + path)  # 将路径名填写框中
            weight_file = path_
            tkm.showinfo(title="提示",
                         message="权重更新成功！")
        else:
            tkm.showinfo(title="提示",
                         message="未选择权重文件执行默认权重！")
    else:
        tkm.showinfo(title="提示",
                     message="未选择权重文件执行默认权重！")


# 权重选择框
weight_Entry = tk.Entry(top,
                        textvariable=weight_path,
                        # state='disable',
                        width=57)
weight_Entry.place(x=210, y=720)
# 实现拖拽
weight_Entry.drop_target_register(DND_FILES)
weight_Entry.dnd_bind('<<Drop>>', dragWeight)
# 权重框初始显示值
weight_path.set("当前权重 ( " + weight_file + " [可拖拽文件到此处] )")
# 权重选择按钮
select_weights_img = PhotoImage(file=select_weights_image)
weight_Btn = tk.Button(top,
                       text=" 选 择 权 重 ",
                       command=selectWeight,
                       relief=RIDGE,
                       image=select_weights_img,
                       bd=0,
                       font=font_Button)
weight_Btn.place(x=button_column_x[3], y=button_column4_y[2])
ToolTip(weight_Btn, msg="选择权重文件(选择.pt/.pth格式的权重文件)", msgFunc=None, follow=True, delay=0)
################################################### 选择权重 End #################################################


################################################### 选择设备【CPU和GPU】 ####################################################
var = tk.IntVar()
# 设备标签
device_Label = tk.Label(top,
                        width=13,
                        height=1,
                        text='empty',
                        font=font_Button)
device_Label.place(x=860, y=115)

device_parameter = {'GPU': '0', 'CPU': 'cpu'}
device_dict = {0: "GPU", 1: "CPU"}

var.set(0)
device_Label.config(text='计算设备 [ ' + device_dict[var.get()] + ' ]')
device_parameter_elem = device_parameter[device_dict[var.get()]]


# 设备选择触发函数
def device_trigger():
    global device_parameter_elem
    device_Label.config(text='计算设备 [ ' + device_dict[var.get()] + ' ]')
    device_parameter_elem = device_parameter[device_dict[var.get()]]


# GPU Radiobutton
GPU_Ra = tk.Radiobutton(top,
                        text='GPU',
                        variable=var,
                        value=0,
                        command=device_trigger,
                        font=font_Button)
GPU_Ra.place(x=1010, y=113)
# CPU Radiobutton
CPU_Ra = tk.Radiobutton(top,
                        text='CPU',
                        variable=var,
                        value=1,
                        command=device_trigger,
                        font=font_Button)
CPU_Ra.place(x=1100, y=113)
################################################### 选择设备【CPU和GPU】 End #################################################


################################################### 选择摄像头设备 ####################################################
var_camdev = tk.IntVar()
# 摄像头标签
l2 = tk.Label(top,
              width=9,
              height=1,
              text='empty',
              font=font_Button)
l2.place(x=1260, y=115)

camera_parameter = {'#1': 0, '#2': 1, '#3': 2}
camera_dict = {0: "#1", 1: "#2", 2: "#3"}
# 设置初始摄像头
var_camdev.set(0)
l2.config(text='摄像头 [ ' + camera_dict[var_camdev.get()] + ' ]')
camera_parameter_elem = camera_parameter[camera_dict[var_camdev.get()]]


# 摄像头选择触发函数
def camera_trigger():
    global camera_parameter_elem
    l2.config(text='摄像头 [ ' + camera_dict[var_camdev.get()] + ' ]')
    camera_parameter_elem = camera_parameter[camera_dict[var_camdev.get()]]

    if var_camdev.get() == 2:
        ip_link_path = tk.StringVar()
        ip_link_Label = tk.Label(top,
                                 width=9,
                                 height=1,
                                 text='IP网络摄像头',
                                 font=font_Button)
        ip_link_Entry = tk.Entry(top,
                                 textvariable=ip_link_path,
                                 width=30)
        # confirm_img = PhotoImage(file=confirm_image)
        ip_link_Btn = tk.Button(top,
                                text="确定",
                                command=lambda: [
                                    [ip_link_path.set(ip_link_path.get()), tkm.showinfo(title="提示", message="更新成功!")]
                                    if ip_link_path.get().startswith(
                                        ('rtsp://', 'rtmp://', 'http://', 'https://'))
                                    else [tkm.showinfo(title="提示", message="链接格式错误!"),
                                          ip_link_path.set("http://admin:admin@10.8.0.204:8081/")]],
                                relief=GROOVE,
                                # image=confirm_img,
                                # bd=0,
                                width=3,
                                height=1,
                                font=font_Label)
        ip_link_Label.place(x=1270, y=50)
        ip_link_Entry.place(x=1270, y=80)

        ip_link_Btn.place(x=1550, y=75)
        # ToolTip(ip_link_Btn, msg="确认(设置用户填写的IP视频链接)", msgFunc=None, follow=True, delay=0)
        ip_link_path.set("http://admin:admin@10.8.0.204:8081/")
        camera_parameter_elem = ip_link_path.get()


# #1 Radiobutton
cam_01_Ra = tk.Radiobutton(top,
                           text='#1',
                           variable=var_camdev,
                           value=0,
                           command=camera_trigger,
                           font=font_Radio)
cam_01_Ra.place(x=1370, y=113)
# #2 Radiobutton
cam_02_Ra = tk.Radiobutton(top,
                           text='#2',
                           variable=var_camdev,
                           value=1,
                           command=camera_trigger,
                           font=font_Radio)
cam_02_Ra.place(x=1445, y=113)
# #3 Radiobutton
cam_03_Ra = tk.Radiobutton(top,
                           text='#3',
                           variable=var_camdev,
                           value=2,
                           command=camera_trigger,
                           font=font_Radio)
cam_03_Ra.place(x=1520, y=113)


################################################### 选择摄像头设备 End #################################################

################################################### 参数列表 ##################################################
def get_params(useCamera=True) -> list:
    params = ['-u', 'detect.py']
    if device_parameter_elem == '0':
        params.append('-d')
        params.append(device_parameter_elem)
    elif device_parameter_elem == 'cpu':
        params.append('-d')
        params.append(device_parameter_elem)

    if click_sound_state == True:
        params.append('-ps')
        params.append('open')
    else:
        params.append('-ps')
        params.append('close')
    params.append('-w')
    params.append(weight_file)
    params.append('--conf-thres')
    params.append(conf_thres)
    params.append('--iou-thres')
    params.append(iou_thres)
    if useCamera:
        params.append('-s')
        params.append('0')

    return params


##################################################### 参数列表 End ############################################


################################################### 打开摄像头 ####################################################
camera_switch = False
open_camera_img = PhotoImage(file=open_camera_image)
close_camera_img = PhotoImage(file=close_camera_image)


def open_camera():
    global camera_switch
    if camera_switch == False:
        try:
            global cap, frame_
            close_radio(True, camera_switch=False)
            cap = cv2.VideoCapture(camera_parameter_elem)  # 创建摄像头对象
        except (Exception, SyntaxError):
            tkm.showinfo(title="提示",
                         message="该相机设备不存在, 自动跳转存在设备！")
        flag = True
        while True:
            if flag:
                flag = False
                camera_switch = True
                open_camera_Btn.config(image=close_camera_img)
            # 读取图像
            ref, frame = cap.read()
            # 摄像头翻转
            frame_ = cv2.flip(frame, 1)
            # tkimg = load_create_iamge(img=frame_, tag='frame', readFlag=False)
            tkimg = imgConvert(frame)
            # 参数1，2是相对于canvas偏移量
            canvas.create_image(0, 0,
                                anchor='nw',
                                image=tkimg)
            temp = tkimg
            top.update()
            top.after(1)

    else:
        open_camera_Btn.config(image=open_camera_img)
        camera_switch = False
        try:
            photo_detection_Btn.config(state='disable')
            cap.release()
            close_radio(False, camera_switch=False)
            canvas.create_image(0, 0,
                                anchor='nw',
                                image=bg)
            tkm.showinfo(title="提示",
                         message="摄像头已关闭！")
        except NameError:
            pass
        finally:
            close_radio(False, camera_switch=False)


# 打开摄像头按钮

open_camera_Btn = tk.Button(top,
                            text='打开摄像头',
                            command=lambda: MyThread(open_camera),
                            relief=RIDGE,
                            image=open_camera_img,
                            bd=0,
                            font=font_Button,
                            )
open_camera_Btn.place(x=button_column_x[0], y=button_column1_y[0])
ToolTip(open_camera_Btn, msg=open_camera_Btn['text'], msgFunc=None, follow=True, delay=0)


################################################### 打开摄像头END ####################################################

################################################### 拍照检测 ####################################################
def photo_detection():
    open_camera_Btn.config(state='normal')
    photo_detection_Btn.config(state='disable')
    close_radio(False, camera_switch=False)
    if not os.path.exists(camera_source):
        os.makedirs(camera_source)
    infile_path = camera_source + 'detect.jpg'
    photo_detection_Btn.config(state='disable')
    # os.remove(file_path)
    global frame_
    cv2.imwrite(infile_path, frame_)
    tkm.showinfo(title="提示",
                 message="照片已保存至" + infile_path)
    canvas.delete('bg_img')
    cap.release()
    detect_camera = cv2.imread(infile_path)
    detect_camera = imgConvert(detect_camera, True)
    params = get_params(useCamera=False)
    params.append('-s')
    params.append(infile_path)
    params.append('--project')
    params.append('photo_detection')
    params.append('--name')
    params.append('result')
    reader = ProcessOutputReader(line_queue, 'python', params=params)
    reader.start()
    global num_runs
    num_runs = -1
    canvas.delete('bg_img')
    tkm.showinfo(title="提示",
                 message="照片已在检测中，注意查看右侧输出及左侧结果！")
    canvas.create_image(0, 0,
                        anchor='nw',
                        image=detect_camera,
                        tag='s_img')
    temp = detect_camera
    top.update()
    top.after(1)
    if time.sleep(6) == None:
        outfile_path = camera_result + 'detect.jpg'
        result_camera = cv2.imread(outfile_path)
        result_camera = imgConvert(result_camera, True)
        canvas.delete('s_img')
        canvas.create_image(0, 0,
                            anchor='nw',
                            image=result_camera,
                            tag='r_img')
        temp = result_camera
        top.update()
        top.after(1)
        if time.sleep(10) == None:
            canvas.delete('r_img')
            canvas.create_image(0, 0,
                                anchor='nw',
                                image=bg)
            temp = bg


# 拍照检测按钮
photos_detect_img = PhotoImage(file=photos_detect_image)
photo_detection_Btn = tk.Button(top,
                                text='拍照检测',
                                command=lambda: MyThread(photo_detection),
                                relief=RIDGE, state='disable',
                                image=photos_detect_img,
                                bd=0,
                                # width=9,
                                # height=2,
                                font=font_Button)
photo_detection_Btn.place(x=button_column_x[0], y=button_column1_y[1])
ToolTip(photo_detection_Btn, msg=photo_detection_Btn['text'] + "(使用前需打开摄像头拍照检测)", msgFunc=None, follow=True, delay=0)


################################################### 拍照检测 End #################################################


################################################### 计时器 End #################################################
def start_time():
    top_clock = tk.Toplevel()
    top_clock.geometry('250x120+1220+375')
    top_clock.title('计时器')  # 窗口标题
    top_clock.resizable(0, 0)  # 禁止缩放
    top_clock.attributes('-alpha', 0.95)
    top_clock.iconbitmap(clock_icon)  # 窗口左上角图标

    def Transpa(value):
        top_clock.attributes('-alpha', value)

    sw = StopWatch(top_clock)
    clock_sca = Scale(top_clock,
                      from_=0,
                      to=1,
                      orient=HORIZONTAL,
                      length=220,
                      showvalue=0,
                      tickinterval=0.2,
                      resolution=0.01,
                      command=Transpa)
    clock_sca.pack(side=BOTTOM)
    clock_sca.set(0.9)
    sw.stopwatch()


# 开始计时按钮
clock_img = PhotoImage(file=clock_image)
start_time_Btn = tk.Button(top,
                           text='计时器',
                           command=lambda: MyThread(start_time),
                           relief=RIDGE,
                           image=clock_img,
                           bd=0,
                           # width=9,
                           # height=2,
                           font=font_Button)
start_time_Btn.place(x=button_column_x[0], y=button_column1_y[2])
ToolTip(start_time_Btn, msg=start_time_Btn['text'], msgFunc=None, follow=True, delay=0)

################################################### 计时器 End #################################################


################################################### 开始检测 ####################################################
num_runs = 0


def start_detect():
    # 先清空内容
    try:
        if os.path.exists('./temp_images'):
            shutil.rmtree('./temp_images')
        if not os.path.exists('./temp_images/'):
            os.mkdir('./temp_images/')
    except FileNotFoundError:
        pass

    global reader
    global num_runs
    try:
        start_detect_Btn.config(state='disable')
        stop_detect_Btn.config(state='normal')

        params = get_params(useCamera=False)
        params.append('-s')
        # camera_parameter_elem = "http://admin:admin@10.8.0.204:8081/"
        params.append(str(camera_parameter_elem))

        reader = ProcessOutputReader(line_queue, 'python', params=params)
        reader.start()
        close_radio(True)
        num_runs = 1
        max = -1
        while True:
            max_ = max
            if os.listdir(images_temp_path):  # 判断文件夹是否为空
                files = os.listdir(images_temp_path)
                for file in files:
                    file_num = int(file.split('.')[0])
                    if file_num > max:
                        # 更新过新文件
                        max = int(file_num)
            # 没更新
            if max_ > 0 and max_ < max:
                filename = images_temp_path + str(max_).zfill(8) + '.jpg'
                dynamic_images = cv2.imread(filename)
                dynamic_images = imgConvert(dynamic_images)
                canvas.create_image(0, 0, anchor='nw', image=dynamic_images)
                # ****将PhotoImage对象保存起来，就不会被临时释放删除，避免闪烁****
                temp = dynamic_images
                top.update()
                top.after(1)
    except EOFError:
        tkm.showerror(title="错误",
                      message="权重文件加载失败，请重新选择！")
    except FileNotFoundError:
        pass;


# 开始检测按钮
start_detect_img = PhotoImage(file=start_detect_image)
start_detect_Btn = tk.Button(top,
                             text='开始检测',
                             command=lambda: Thread(target=start_detect).start(),
                             relief=RIDGE,
                             image=start_detect_img,
                             bd=0,
                             # width=9,
                             # height=2,
                             font=font_Button)
start_detect_Btn.place(x=button_column_x[0], y=button_column1_y[3])
ToolTip(start_detect_Btn, msg=start_detect_Btn['text'] + "(使用摄像头实时检测)", msgFunc=None, follow=True, delay=0)


################################################### 开始检测 End #################################################


################################################### 停止检测 ####################################################
def stop_detect():
    try:
        start_detect_Btn.config(state='normal')
        stop_detect_Btn.config(state='disable')
        reader.stop()
        reader.join(timeout=5)  # give thread a chance to exit gracefully
        canvas.create_image(0, 0, anchor='nw', image=bg)
        close_radio(False)
        tkm.showinfo(title="提示",
                     message="已停止检测！")

        if os.listdir(images_temp_path):
            file_list = os.listdir(images_temp_path)
            for file in file_list:
                image_temp_path = images_temp_path + file
                os.remove(image_temp_path)

        if reader.is_alive():
            raise RuntimeError("process output reader failed to stop")
    except RuntimeError:
        tkm.showinfo(title="提示",
                     message="检测已运行，请勿重复点击！")
    except (UnicodeDecodeError, NameError):
        pass
    finally:
        start_detect_Btn.config(state='normal')
        stop_detect_Btn.config(state='disable')
        close_radio(False)


# 停止检测按钮
pause_detect_img = PhotoImage(file=pause_detect_image)
stop_detect_Btn = tk.Button(top,
                            text='停止检测',
                            command=lambda: MyThread(stop_detect),
                            relief=RIDGE,
                            state='disable',
                            image=pause_detect_img,
                            bd=0,
                            # width=9,
                            # height=2,
                            font=font_Button)
stop_detect_Btn.place(x=button_column_x[0], y=button_column1_y[4])
ToolTip(stop_detect_Btn, msg=stop_detect_Btn['text'], msgFunc=None, follow=True, delay=0)


################################################### 停止检测 End #################################################


################################################### 当前结果 ####################################################
def current_result():
    result_dir_name = './runs/detect/'
    current_path = os.getcwd()
    result_dir_name = current_path + result_dir_name
    try:
        if os.listdir(result_dir_name):  # 获取文件夹内容
            files = os.listdir(result_dir_name)
            index = [1]
            for dir_file in files:
                dir_file = dir_file[3:]
                if dir_file != '':
                    dir_file = int(dir_file)
                    index.append(dir_file)
        sort_index = sorted(index)
        max_index = sort_index[-1]
        # print(sort_index)
        if max_index and max_index != 1:
            if num_runs == 0:
                tkm.showinfo(title="提示",
                             message="未运行程序，当前无检测结果！")
            elif num_runs == -1:
                current_path = os.getcwd()
                camera_result_path = current_path + '/photo_detection/result/'
                os.startfile(camera_result_path)
            else:
                for num in range(num_runs):
                    path = result_dir_name + 'exp' + str(max_index)
                    os.startfile(path)
                    del sort_index[-1]
                    max_index = sort_index[-1]
        elif max_index == 1:
            path = result_dir_name + 'exp'
            os.startfile(path)
            time.sleep(0.25)

    except UnboundLocalError:
        tkm.showinfo(title="提示",
                     message="检测结果为空！")


# 当前结果按钮
current_result_img = PhotoImage(file=current_result_image)
current_result_Btn = tk.Button(top,
                               text="本次结果",
                               command=lambda: MyThread(current_result),
                               relief=RIDGE,
                               image=current_result_img,
                               bd=0,
                               # width=9,
                               # height=2,
                               font=font_Button)
current_result_Btn.place(x=button_column_x[0], y=button_column1_y[5])
ToolTip(current_result_Btn, msg=current_result_Btn['text'] + '(浏览本次程序运行所生成的结果)', msgFunc=None, follow=True, delay=0)


################################################### 当前结果 End #################################################


################################################### 清空缓存 ####################################################
def clear_cache():
    if tkm.askokcancel(title='清空缓存',
                       message="是否清空程序缓存\n"
                               "(包括运行日志和图片缓存，当运行结果复制不正确需要此功能)"):
        file_list = os.listdir(images_temp_path)
        for file in file_list:
            image_temp_path = images_temp_path + file
            os.remove(image_temp_path)
        if os.path.exists('run.log'):
            os.remove("run.log")
        if os.path.exists('./photo_detection/'):
            shutil.rmtree('./photo_detection/')
        if not os.listdir(images_temp_path) and not os.path.exists("run.log") \
                and not os.path.exists('./photo_detection/'):
            tkm.showinfo(title="提示",
                         message="缓存已清空！")


# 清空缓存按钮
empty_img = PhotoImage(file=empty_image)
clear_cache_Btn = tk.Button(top,
                            text="清空缓存",
                            command=lambda: MyThread(clear_cache),
                            relief=RIDGE,
                            image=empty_img,
                            bd=0,
                            # width=9,
                            # height=2,
                            font=font_Button)
clear_cache_Btn.place(x=button_column_x[0], y=button_column1_y[6])
ToolTip(clear_cache_Btn, msg=clear_cache_Btn['text'] + '(清除程序运行保存的文件)', msgFunc=None, follow=True, delay=0)


################################################### 清空缓存 End #################################################


################################################### 重置画布 ####################################################
def reset_canvas():
    if tkm.askokcancel(title='重置画布',
                       message="是否将画布重置到初始状态(当画布显示不正确需要此功能)"):
        canvas.delete('All')
        canvas.create_image(0, 0,
                            anchor='nw',
                            image=bg,
                            tag='bg_img')
        tkm.showinfo(title="提示",
                     message="画布已重置！")


# 重置画布按钮
reset_canvas_img = PhotoImage(file=reset_canvas_image)
reset_canvas_Btn = tk.Button(top,
                             text="重置画布",
                             command=lambda: MyThread(reset_canvas),
                             relief=RIDGE,
                             image=reset_canvas_img,
                             bd=0,
                             # width=9,
                             # height=2,
                             font=font_Button)
reset_canvas_Btn.place(x=button_column_x[0], y=button_column1_y[7])
ToolTip(reset_canvas_Btn, msg=reset_canvas_Btn['text'] + '(当画布显示不正常时使用)', msgFunc=None, follow=True, delay=0)


################################################### 重置画布 End #################################################


################################################### 重置按钮状态 ####################################################
def reset_Btn_state():
    if tkm.askokcancel(title='重置按钮状态',
                       message="是否重置按钮到初始状态(非必要请勿点击)"):
        close_radio(False)
        open_camera_Btn.config(state='normal')
        photo_detection_Btn.config(state='disable')
        start_time_Btn.config(state='normal')
        start_detect_Btn.config(state='normal')
        stop_detect_Btn.config(state='disable')
        tkm.showinfo(title="提示",
                     message="按钮状态已重置！")


# 重置状态按钮
reset_button_img = PhotoImage(file=reset_button_image)
reset_Btn_state_Btn = tk.Button(top,
                                text="重置按钮状态",
                                command=lambda: MyThread(reset_Btn_state),
                                relief=RIDGE,
                                image=reset_button_img,
                                bd=0,
                                # width=9,
                                # height=2,
                                font=font_Button)
reset_Btn_state_Btn.place(x=button_column_x[0], y=button_column1_y[8])
ToolTip(reset_Btn_state_Btn, msg=reset_Btn_state_Btn['text'] + '(当按钮状态异常时使用)', msgFunc=None, follow=True, delay=0)
################################################### 重置按钮状态 End #################################################


################################################### 创建输出文本窗口 ####################################################
line_queue = Queue(maxsize=2000)
console = MyConsole(top, line_queue)
console.place(x=860, y=150)
################################################### 创建输出文本窗口 End #################################################


################################################### 选择文件夹 ##################################################
dir_path = tk.StringVar()


# 拖拽文件夹
def dragDir(event):
    global selecct_dir_path
    selecct_dir_path = event.data

    # 拖入多文件标志
    mulFile_flag = False
    # 拖入单文件标志
    sinFile_flag = False
    # 判断变量
    flag = False
    count_image = 0
    count_video = 0
    global num_runs  # 全局变量 检测过文件的个数

    if len(event.data.split(' ')) > 1:
        mulFile_flag = True
    if event.data.split(' ')[0].find('.') != -1:
        sinFile_flag = True

    if mulFile_flag == False and sinFile_flag == False:

        dir_path.set(event.data)

        try:
            if os.listdir(selecct_dir_path):  # 判断文件夹是否为空
                files = os.listdir(selecct_dir_path)
                for file in files:
                    fulldirct = os.path.join(selecct_dir_path, file)
                    if os.path.isfile(fulldirct):
                        suffix = file.split('.')[1]
                        if suffix in image_format:
                            count_image += 1
                        elif suffix in video_format:
                            count_video += 1
                        else:
                            flag = True
            count = count_video + count_image
            if count == 0:
                num_runs = 0
                tkm.showinfo(title='提示',
                             message='该文件夹内无图像或视频文件，请重新选择！')
            elif flag == True:
                num_runs = 0
                tkm.showinfo(title='提示',
                             message='该文件夹有不支持的文件格式')
            else:
                dir_path.set(selecct_dir_path)
                # 是否确认开始检测窗口
                global message_Isdetectdir
                message_Isdetectdir = "共检测到" + str(count_image) + \
                                      "张图片和" + str(count_video) + "个视频文件，确认开始检测？"
                tkm.showinfo(title="提示",
                             message=message_Isdetectdir[:-8])
        except FileNotFoundError:
            num_runs = 0
            dir_path.set("文件夹检测( 将要检测的图片或视频放到检测的文件夹下 )")
            tkm.showinfo(title="提示",
                         message="未选择文件！")

    elif mulFile_flag == True:
        tkm.showinfo(title="提示",
                     message="多文件检查请拖入下行！")
    elif sinFile_flag == True:
        tkm.showinfo(title="提示",
                     message="单文件检查请拖入下行！")


# 选择文件夹
def selectDir():
    # 选择
    global selecct_dir_path
    selecct_dir_path = tkf.askdirectory()
    # 判断
    flag = False
    count_image = 0
    count_video = 0
    global num_runs  # 全局变量 检测过文件的个数
    try:
        if os.listdir(selecct_dir_path):  # 判断文件夹是否为空
            files = os.listdir(selecct_dir_path)
            for file in files:
                fulldirct = os.path.join(selecct_dir_path, file)
                if os.path.isfile(fulldirct):
                    suffix = file.split('.')[1]
                    if suffix in image_format:
                        count_image += 1
                    elif suffix in video_format:
                        count_video += 1
                    else:
                        flag = True
        count = count_video + count_image
        if count == 0:
            num_runs = 0
            tkm.showinfo(title='提示',
                         message='该文件夹内无图像或视频文件，请重新选择！')
        elif flag == True:
            num_runs = 0
            tkm.showinfo(title='提示',
                         message='该文件夹有不支持的文件格式')
        else:
            dir_path.set(selecct_dir_path)
            # 是否确认开始检测窗口
            global message_Isdetectdir
            message_Isdetectdir = "共检测到" + str(count_image) + \
                                  "张图片和" + str(count_video) + "个视频文件，确认开始检测？"
            tkm.showinfo(title="提示",
                         message=message_Isdetectdir[:-8])
    except FileNotFoundError:
        num_runs = 0
        dir_path.set("文件夹检测( 将要检测的图片或视频放到检测的文件夹下 )")
        tkm.showinfo(title="提示",
                     message="未选择文件！")


# 检测文件夹
def detectDir():
    global num_runs  # 全局变量 检测过文件的个数
    try:
        if tkm.askokcancel(title='开始检测',
                           message=message_Isdetectdir):
            num_runs = 1
            tkm.showinfo(title="提示",
                         message="检测中，关闭该窗口查看右侧文本输出窗口查看程序输出结果！")

            params = get_params(useCamera=False)
            params.append('-s')
            params.append(selecct_dir_path)

            reader = ProcessOutputReader(line_queue, 'python', params=params)
            reader.start()
    except NameError:
        tkm.showinfo(title="提示",
                     message="请先选择文件夹！")


# 文件夹检测路径框
dir_Entry = tk.Entry(top,
                     textvariable=dir_path,
                     # state='disable',
                     width=57)
dir_Entry.place(x=210, y=620)
dir_path.set("文件夹检测( 选择包含图片和视频的文件夹[可拖拽文件夹到此处] )")
# 拖拽文件夹
dir_Entry.drop_target_register(DND_FILES)
dir_Entry.dnd_bind('<<Drop>>', dragDir)
# 文件夹选择按钮
select_file_img = PhotoImage(file=select_file_image)
select_dir_Btn = tk.Button(top,
                           text="选择",
                           command=selectDir,
                           relief=RIDGE,
                           image=select_file_img,
                           bd=0,
                           font=font_Button)
select_dir_Btn.place(x=button_column_x[3], y=button_column4_y[0])
ToolTip(select_dir_Btn, msg='选择文件夹', msgFunc=None, follow=True, delay=0)
# 文件夹检测按钮
start_run_img = PhotoImage(file=start_run_image)
detect_dir_Btn = tk.Button(top,
                           text="检测",
                           command=detectDir,
                           relief=RIDGE,
                           image=start_run_img,
                           bd=0,
                           font=font_Button)
detect_dir_Btn.place(x=button_column_x[4], y=button_column4_y[0])
ToolTip(detect_dir_Btn, msg=detect_dir_Btn['text'], msgFunc=None, follow=True, delay=0)
################################################### 选择文件夹 End #################################################


################################################### 选择文件 #######################################################
file_path = tk.StringVar()


# 拖拽文件
def dragFile(event):
    global selecct_file_path

    selecct_file_path = tuple(event.data.split(' '))

    path_list = event.data.split(' ')
    img_vid_format = image_format + video_format
    # 存在其他格式标志
    exist_flag = False
    for file in path_list:
        if file.split('.')[-1] not in img_vid_format:
            # 存在无法检测的格式
            exist_flag = True
    # 检测放入文件夹
    dir_flag = False
    if event.data.find('.') == -1:
        dir_flag = True
        print('文件夹')

    if exist_flag == False and dir_flag == False:
        file_path.set(event.data)
        # 选择
        global num_runs  # 记录选择的文件个数

        # 判断
        count_image = 0
        count_video = 0
        for file in selecct_file_path:
            file_format = file.split('.')[1]
            if file_format in image_format:
                count_image += 1
            elif file_format in video_format:
                count_video += 1
        count = count_image + count_video

        if count != 0:
            file_path.set(selecct_file_path)
            global path_
            path_ = ''
            for index in range(len(selecct_file_path)):
                path_ += selecct_file_path[index] + ' '
            path_ = path_[:-1].replace('/', '\\')
            path_ = path_.split(' ')
            # 检测
            global message_IsdetectFile
            message_IsdetectFile = "共检测到" + str(count_image) + \
                                   "张图片和" + str(count_video) + \
                                   "个视频文件，确认开始检测？"
            tkm.showinfo(title="提示",
                         message=message_IsdetectFile[:-8])
    elif dir_flag == True:
        tkm.showinfo(title="提示",
                     message='文件夹请拖入上行！')
    elif exist_flag == True:
        tkm.showinfo(title="提示",
                     message='文件格式非图片或视频，请重新选择！')


# 选择文件
def selectFile():
    # 选择
    global num_runs  # 记录选择的文件个数

    _image_format = ""
    _video_format = ""

    for image_format_ele in image_format:
        _image_format += '*.' + image_format_ele + ' '
    _image_format = _image_format[:-1]

    for video_format_ele in video_format:
        _video_format += '*.' + video_format_ele + ' '
    _video_format = _video_format[:-1]

    path = tkf.askopenfilenames(
        filetypes=[("image file", _image_format),
                   ("video file", _video_format)])
    # 判断
    count_image = 0
    count_video = 0
    for file in path:
        file_format = file.split('.')[1]
        if file_format in image_format:
            count_image += 1
        elif file_format in video_format:
            count_video += 1
    count = count_image + count_video

    if count != 0:
        file_path.set(path)
        global path_
        path_ = ''
        for index in range(len(path)):
            path_ += path[index] + ' '
        path_ = path_[:-1].replace('/', '\\')
        path_ = path_.split(' ')
        # 检测
        global message_IsdetectFile
        message_IsdetectFile = "共检测到" + str(count_image) + \
                               "张图片和" + str(count_video) + \
                               "个视频文件，确认开始检测？"
        tkm.showinfo(title="提示",
                     message=message_IsdetectFile[:-8])


# 检测文件
def detectFile():
    global num_runs
    try:
        if tkm.askokcancel(title='开始检测',
                           message=message_IsdetectFile):
            params = get_params(useCamera=False)
            params.append('-s')
            for path_ele in path_:
                params.append(path_ele)
            num_runs = len(path_)
            tkm.showinfo(title="提示",
                         message="检测中，关闭该窗口查看右侧文本输出窗口查看程序输出结果！")

            reader = ProcessOutputReader(line_queue, 'python', params=params)
            reader.start()
        else:
            num_runs = 0
            tkm.showinfo(title="提示",
                         message="用户取消检测！")
    except NameError:
        tkm.showinfo(title="提示",
                     message="请先选择文件！")


file_Entry = tk.Entry(top,
                      textvariable=file_path,
                      # state='disable',
                      width=57)
file_Entry.place(x=210, y=670)

file_Entry.drop_target_register(DND_FILES)
file_Entry.dnd_bind('<<Drop>>', dragFile)

file_path.set("文件检测(  选择图片和视频[可多选、可拖拽文件到此处]  )")

select_file_Btn = tk.Button(top,
                            text="选择",
                            command=selectFile,
                            relief=RIDGE,
                            image=select_file_img,
                            bd=0,
                            font=font_Button)
select_file_Btn.place(x=button_column_x[3], y=button_column4_y[1])
ToolTip(select_file_Btn, msg='选择图像等文件', msgFunc=None, follow=True, delay=0)

detect_file_Btn = tk.Button(top,
                            text="检测",
                            command=detectFile,
                            relief=RIDGE,
                            image=start_run_img,
                            bd=0,
                            font=font_Button)
detect_file_Btn.place(x=button_column_x[4], y=button_column4_y[1])
ToolTip(detect_file_Btn, msg=detect_file_Btn['text'], msgFunc=None, follow=True, delay=0)
################################################### 选择文件 End #################################################


################################################### 链接检测 ####################################################
link_path = tk.StringVar()


def selectPath():
    # 选择
    global num_runs  # 记录选择的文件个数
    global reader

    message_Islink_path = "确认开始检测链接：" + link_path.get()

    if tkm.askokcancel(title='开始检测',
                       message=message_Islink_path):

        print(link_path.get())
        if link_path.get().startswith("https://") or \
                link_path.get().startswith("http://"):
            num_runs = 1
            params = get_params(useCamera=False)
            params.append('-s')
            params.append(link_path.get())
            try:
                close_radio(True)
                start_detect_Btn.config(state='disable')
                stop_detect_Btn.config(state='normal')
                reader = ProcessOutputReader(line_queue, 'python', params=params)
                reader.start()
                close_radio(False)
            except AssertionError:
                num_runs = 0

                tkm.showinfo(title='提示',
                             message="该链接不是视频链接，请重新输入！")

            close_radio(True)
            start_detect_Btn.config(state='disable')
            stop_detect_Btn.config(state='normal')
        else:
            num_runs = 0
            tkm.showinfo(title='提示',
                         message="链接格式不正确，请重新输入！")
    else:
        num_runs = 0
        tkm.showinfo(title="提示",
                     message="用户取消检测！")


link_Entry = tk.Entry(top,
                      textvariable=link_path,
                      width=57)
link_Entry.place(x=210, y=770)

link_path.set("链接检测( 放入单个视频网页链接 )")

link_detect_img = PhotoImage(file=link_detect_image)
link_Btn = tk.Button(top,
                     text=" 链 接 检 测 ",
                     command=selectPath,
                     relief=RIDGE,
                     image=link_detect_img,
                     bd=0,
                     font=font_Button)
link_Btn.place(x=button_column_x[3], y=button_column4_y[3])
ToolTip(link_Btn, msg="链接检测(输入图像或视频的链接进行检测)", msgFunc=None, follow=True, delay=0)


################################################### 链接检测 End ####################################################


################################################### 清空文本 ####################################################
def delete_text():
    console.delete('1.0', END)
    tkm.showinfo(title='提示',
                 message="清空成功！")


clear_img = PhotoImage(file=clear_image)
delete_text_Btn = tk.Button(top,
                            text='清空文本',
                            command=delete_text,
                            relief=RIDGE,
                            image=clear_img,
                            bd=0,
                            # width=9,
                            # height=2,
                            font=font_Button)
delete_text_Btn.place(x=button_column_x[6], y=button_right_y[0])
ToolTip(delete_text_Btn, msg=delete_text_Btn['text'] + "(清空程序输出窗口中的文本内容)", msgFunc=None, follow=True, delay=0)


################################################### 清空文本 End #################################################


################################################### 复制结果 ####################################################
def copy_run_result():
    if not os.path.exists(run_resut_path):
        tkm.showinfo(title='提示',
                     message="当前还未产生结果，请运行程序后再复制！")
        return
    else:
        f = open(run_resut_path, 'r')
        result_lines = f.readlines()
        if result_lines == []:
            if tkm.askokcancel(title='复制结果',
                               message="当前运行结果为空，是否继续复制！"):
                pyperclip.copy('说了空的你还不信！v(￣_￣|||)')
                spam = pyperclip.paste()
                tkm.showinfo(title='提示',
                             message="复制成功！")
            else:
                tkm.showinfo(title='提示',
                             message="用户取消！")
        else:
            temp_run_result = ''
            for result_line in result_lines:
                temp_run_result += result_line
            pyperclip.copy(temp_run_result)
            spam = pyperclip.paste()
            tkm.showinfo(title='提示',
                         message="复制成功！")


copy_result_img = PhotoImage(file=copy_result_image)
copy_run_result_Btn = tk.Button(top,
                                text='复制结果',
                                command=copy_run_result,
                                relief=RIDGE,
                                image=copy_result_img,
                                bd=0,
                                # width=9,
                                # height=2,
                                font=font_Button)
copy_run_result_Btn.place(x=button_column_x[8], y=button_right_y[0])
ToolTip(copy_run_result_Btn, msg=copy_run_result_Btn['text'] + "(复制程序输出窗口中的文本内容到剪贴板)", msgFunc=None, follow=True,
        delay=0)


################################################### 复制结果 End #################################################


################################################### 训练结果 ####################################################
def train_dir():
    current_path = os.getcwd()
    absolute_path = current_path + '/runs/train/'
    try:
        os.startfile(absolute_path)
    except FileNotFoundError:
        tkm.showinfo(title='提示',
                     message="当前目录为空！")


train_result_img = PhotoImage(file=train_result_image)
open_train_dir_Btn = tk.Button(top,
                               text='训练结果',
                               command=train_dir,
                               relief=RIDGE,
                               image=train_result_img,
                               bd=0,
                               # width=9,
                               # height=2,
                               font=font_Button)
open_train_dir_Btn.place(x=button_column_x[5], y=button_right_y[1])
ToolTip(open_train_dir_Btn, msg=open_train_dir_Btn['text'] + "(打开程序训练结果文件夹)", msgFunc=None, follow=True, delay=0)


################################################### 训练结果 End ################################################


################################################### 生成训练命令 ####################################################
def train_cmd():
    train_cmd_Btn.config(state='disable')
    entry_width = 30
    entry_x = 100
    entry_x_interval = 435
    entry_y = 50
    entry_y_interval = 50
    label_x = -20
    label_x_interval = 430
    label_y = 50
    label_y_interval = 50

    top_train = tk.Toplevel()
    top_train.iconbitmap(train_cmd_icon)  # 窗口左上角图标
    top_train.resizable(0, 0)  # 禁止缩放
    top_train.title("生成训练命令")
    top_train.geometry('830x450+500+320')

    # parser.add_argument('--weights', '-w', type=str, default='weights/yolov3-tiny.pt', help='initial weights path')
    # tk.Entry
    weight_Label = tk.Label(top_train,
                            width=13,
                            height=1,
                            text='预训练权重',
                            font=font_Label)
    weight_Label.place(x=label_x, y=label_y)
    weight_path = tk.StringVar()
    weight_Entry = tk.Entry(top_train,
                            textvariable=weight_path,
                            width=entry_width)
    weight_Entry.place(x=entry_x, y=entry_y)
    weight_path.set('weights/yolov3-tiny.pt')

    # parser.add_argument('--cfg', type=str, default='models/yolov3-tiny.yaml', help='model.yaml path')
    # tk.Entry
    cfg_Label = tk.Label(top_train,
                         width=13,
                         height=1,
                         text='模型文件',
                         font=font_Label)
    cfg_Label.place(x=label_x, y=label_y + label_y_interval)

    cfg_path = tk.StringVar()
    cfg_Entry = tk.Entry(top_train,
                         textvariable=cfg_path,
                         width=entry_width)
    cfg_Entry.place(x=entry_x, y=entry_y + entry_y_interval)
    cfg_path.set('models/yolov3-tiny.yaml')

    # parser.add_argument('--data', type=str, default='data/2classes/dataset.yaml', help='data.yaml path')
    # tk.Entry
    data_Label = tk.Label(top_train,
                          width=13,
                          height=1,
                          text='数据集文件',
                          font=font_Label)
    data_Label.place(x=label_x, y=label_y + 2 * label_y_interval)
    data_path = tk.StringVar()
    data_Entry = tk.Entry(top_train,
                          textvariable=data_path,
                          width=entry_width)
    data_Entry.place(x=entry_x, y=entry_y + 2 * entry_y_interval)
    data_path.set('data/dataset.yaml')

    # parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')
    # tk.Entry
    hyp_Label = tk.Label(top_train,
                         width=13,
                         height=1,
                         text='超参数文件',
                         font=font_Label)
    hyp_Label.place(x=label_x, y=label_y + 3 * label_y_interval)
    hyp_path = tk.StringVar()
    hyp_Entry = tk.Entry(top_train,
                         textvariable=hyp_path,
                         width=entry_width)
    hyp_Entry.place(x=entry_x, y=entry_y + 3 * entry_y_interval)
    hyp_path.set('data/hyp.scratch.yaml')

    # parser.add_argument('--epochs', '-e', type=int, default=1)
    #  tk.Spinbox
    epochs_Label = tk.Label(top_train,
                            width=13,
                            height=1,
                            text='训练轮数',
                            font=font_Label)
    epochs_Label.place(x=label_x, y=label_y + 4 * label_y_interval)
    epochs_tuple = tuple([x for x in range(10, 310, 10)])
    epochs_Spbox = tk.Spinbox(top_train, values=epochs_tuple, width=entry_width - 1)
    epochs_Spbox.place(x=entry_x, y=entry_y + 4 * entry_y_interval)

    # parser.add_argument('--batch-size', '-b', type=int, default=1, help='total batch size for all GPUs')
    #  tk.Spinbox
    batch_size_Label = tk.Label(top_train,
                                width=13,
                                height=1,
                                text='批量大小',
                                font=font_Label)
    batch_size_Label.place(x=label_x, y=label_y + 5 * label_y_interval)
    epochs_tuple = tuple([2 ** x for x in range(11)])
    batch_size_Spbox = tk.Spinbox(top_train, values=epochs_tuple, width=entry_width - 1)
    batch_size_Spbox.place(x=entry_x, y=entry_y + 5 * entry_y_interval)

    # parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')
    # tk.Spinbox
    img_size_Label = tk.Label(top_train,
                              width=13,
                              height=1,
                              text='图像尺寸大小',
                              font=font_Label)
    img_size_Label.place(x=label_x + label_x_interval, y=label_y + label_y_interval)
    img_size_train_Label = tk.Label(top_train,
                                    width=13,
                                    height=1,
                                    text='train',
                                    font=font_Label)
    img_size_train_Label.place(x=label_x + label_x_interval + 80, y=label_y + label_y_interval - 30)

    img_size_tuple = tuple([40 * x for x in range(1, 100)])
    img_size_train_Spbox = tk.Spinbox(top_train, values=img_size_tuple, width=entry_width - 25)
    img_size_train_Spbox.place(x=entry_x + entry_x_interval, y=entry_y + entry_y_interval)

    img_size_train_Label = tk.Label(top_train,
                                    width=13,
                                    height=1,
                                    text='test',
                                    font=font_Label)
    img_size_train_Label.place(x=label_x + label_x_interval + 180, y=label_y + label_y_interval - 30)

    img_size_test_Spbox = tk.Spinbox(top_train, values=img_size_tuple, width=entry_width - 25)
    img_size_test_Spbox.place(x=entry_x + entry_x_interval + 100, y=entry_y + entry_y_interval)

    # parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')
    # tk.Spinbox
    workers_Label = tk.Label(top_train,
                             width=13,
                             height=1,
                             text='线程数',
                             font=font_Label)
    workers_Label.place(x=label_x + label_x_interval, y=label_y + 2 * label_y_interval)
    workers_tuple = tuple([x for x in range(1, 129)])
    workers_Spbox = tk.Spinbox(top_train, values=workers_tuple, width=entry_width - 1)
    workers_Spbox.place(x=entry_x + entry_x_interval, y=entry_y + 2 * entry_y_interval)

    # parser.add_argument('--project', default='runs/train', help='save to project/name')
    # tk.Entry
    project_Label = tk.Label(top_train,
                             width=13,
                             height=1,
                             text='保存项目至',
                             font=font_Label)
    project_Label.place(x=label_x + label_x_interval, y=label_y + 3 * label_y_interval)
    project_path = tk.StringVar()
    project_Entry = tk.Entry(top_train,
                             textvariable=project_path,
                             width=entry_width)
    project_Entry.place(x=entry_x + entry_x_interval, y=entry_y + 3 * entry_y_interval)
    project_path.set('runs/train')

    # parser.add_argument('--name', default='exp', help='save to project/name')
    # tk.Entry
    name_Label = tk.Label(top_train,
                          width=13,
                          height=1,
                          text='保存项目名',
                          font=font_Label)
    name_Label.place(x=label_x + label_x_interval, y=label_y + 4 * label_y_interval)
    name_path = tk.StringVar()
    name_Entry = tk.Entry(top_train,
                          textvariable=name_path,
                          width=entry_width)
    name_Entry.place(x=entry_x + entry_x_interval, y=entry_y + 4 * entry_y_interval)
    name_path.set('exp')

    # parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
    # tk.Checkbutton
    cache_images_flag = False

    def fun_cache_images_cbtn():
        global cache_images_flag
        if cache_images_var.get() == 1:
            cache_images_flag = True
        else:
            cache_images_flag = False

    cache_images_var = tk.IntVar()
    cache_images_cbtn = tk.Checkbutton(top_train, text='图像缓存', variable=cache_images_var, onvalue=1, offvalue=0,
                                       command=fun_cache_images_cbtn)
    cache_images_cbtn.place(x=label_x + label_x_interval + 40, y=label_y + 5 * label_y_interval - 6)
    cache_images_var.set(0)

    # parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
    # tk.Checkbutton
    image_weights_flag = False

    def fun_image_weights_cbtn():
        global image_weights_flag
        if image_weights_var.get() == 1:
            image_weights_flag = True
        else:
            image_weights_flag = False

    image_weights_var = tk.IntVar()
    image_weights_cbtn = tk.Checkbutton(top_train, text='图像权重', variable=image_weights_var, onvalue=1, offvalue=0,
                                        command=fun_image_weights_cbtn)
    image_weights_cbtn.place(x=label_x + label_x_interval + 180, y=label_y + 5 * label_y_interval - 6)
    image_weights_var.set(0)

    # parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters[in 584 modify epochs]')
    # tk.Checkbutton
    evolve_flag = False

    def fun_evolve_cbtn():
        global evolve_flag
        if evolve_var.get() == 1:
            evolve_flag = True
        else:
            evolve_flag = False

    evolve_var = tk.IntVar()
    evolve_cbtn = tk.Checkbutton(top_train, text='参数进化', variable=evolve_var, onvalue=1, offvalue=0,
                                 command=fun_evolve_cbtn)
    evolve_cbtn.place(x=label_x + label_x_interval + 310, y=label_y + 5 * label_y_interval - 6)
    evolve_var.set(0)

    # 输出结果
    result_Label = tk.Label(top_train,
                            width=90,
                            height=5,
                            text='\t\t\t\t\t此处生成训练命令',
                            font=font_Label,
                            bg='yellow', anchor="w", justify="left"
                            )
    result_Label.place(x=0, y=335)

    def generate():
        global cmd
        cmd = 'python train.py'
        cmd += '         --weights ' + weight_Entry.get()
        cmd += '  --cfg ' + cfg_Entry.get()
        cmd += '\n\t\t  --data ' + data_Entry.get()
        cmd += '  --hyp ' + hyp_Entry.get()
        cmd += '  --epochs ' + epochs_Spbox.get()
        cmd += '\n\t\t  --batch-size ' + batch_size_Spbox.get()
        cmd += '  --img-size [' + img_size_train_Spbox.get() + ',' + img_size_test_Spbox.get() + ']'
        cmd += '\n\t\t  --workers ' + workers_Spbox.get()
        cmd += '  --project ' + project_Entry.get()
        cmd += '\n\t\t  --name ' + name_Entry.get()

        if cache_images_var.get():
            cmd += '  --cache-images'
        if image_weights_var.get():
            cmd += '  --image-weights'
        if evolve_var.get():
            cmd += '  --evolve'
        result_Label.config(text=cmd)
        copy_Btn.config(state='normal')

    generate_Btn = tk.Button(top_train,
                             text='生成命令',
                             command=generate,
                             relief=RIDGE,
                             width=6,
                             height=1,
                             font=font_Button)
    generate_Btn.place(x=535, y=20)

    # 复制命令
    def copy_reslut():
        try:
            # 匹配非字符分割
            cmd_list = re.split(r"\s", cmd)
            new_cmd = ''
            for x in cmd_list:
                if x != '':
                    new_cmd += x + ' '
            pyperclip.copy(new_cmd[:-1])
            spam = pyperclip.paste()
            copy_Btn.config(state='disable')
        except NameError:
            copy_Btn.config(state='disable')

    copy_Btn = tk.Button(top_train,
                         text='复制命令',
                         command=copy_reslut,
                         state='disable',
                         relief=RIDGE,
                         width=6,
                         height=1,
                         font=font_Button)
    copy_Btn.place(x=730, y=20)

    # 点击x关闭事件
    def top_level_close():
        train_cmd_Btn.config(state='normal')
        top_train.destroy()

    top_train.protocol("WM_DELETE_WINDOW", top_level_close)

    # 点击x关闭事件
    def top_level_close():
        train_cmd_Btn.config(state='normal')
        top_train.destroy()

    top_train.protocol("WM_DELETE_WINDOW", top_level_close)


train_cmd_img = PhotoImage(file=train_cmd_image)
train_cmd_Btn = tk.Button(top,
                          text='生成训练命令',
                          command=train_cmd,
                          relief=RIDGE,
                          image=train_cmd_img,
                          bd=0,
                          # width=9,
                          # height=2,
                          font=font_Button)
train_cmd_Btn.place(x=button_column_x[7], y=button_right_y[1])
ToolTip(train_cmd_Btn, msg=train_cmd_Btn['text'] + "(生成用户所需的训练命令供服务器使用)", msgFunc=None, follow=True, delay=0)


################################################### 生成训练命令 End #################################################


################################################### 检测结果 ####################################################
def detect_result():
    current_path = os.getcwd()
    absolute_path = current_path + '/runs/detect/'
    try:
        os.startfile(absolute_path)
    except FileNotFoundError:
        tkm.showinfo(title='提示',
                     message="当前目录为空！")


detect_result_img = PhotoImage(file=detect_result_image)
all_result_dir_Btn = tk.Button(top,
                               text='检测结果',
                               command=lambda: MyThread(detect_result),
                               relief=RIDGE,
                               image=detect_result_img,
                               bd=0,
                               # width=9,
                               # height=2,
                               font=font_Button)
all_result_dir_Btn.place(x=button_column_x[9], y=button_right_y[1])
ToolTip(all_result_dir_Btn, msg=all_result_dir_Btn['text'] + "(打开程序全部检测结果的文件夹)", msgFunc=None, follow=True, delay=0)


################################################### 检测结果 End #################################################


################################################### Conda工具 ####################################################
def conda_tool():
    btn_Width = 9
    main_index_dict = {'pip list': 2, 'conda list': 3, 'conda env list': 2}
    current_env = 'base'
    env_all_list = ['base']
    row_list = [36, 120, 220, 330, 360, 460, 490, 560, 630]  # 控件所在行值
    conda_tool_Btn.config(state='disable')
    top_conda = tk.Toplevel()
    top_conda.geometry('1200x700+350+120')
    top_conda.title('Conda工具')  # 窗口标题
    top_conda.resizable(0, 0)  # 禁止缩放
    top_conda.attributes('-alpha', 0.95)
    top_conda.iconbitmap('./load_file/icon/conda.ico')  # 窗口左上角图标
    ######################################################## 滚动条 #######################################################
    sc = tk.Scrollbar(top_conda)
    sc.pack(side=RIGHT, fill=Y)
    # 列表动，滚动条跟着动
    getRow = tk.StringVar()
    lb = tk.Listbox(top_conda,
                    width=52,
                    yscrollcommand=sc.set,
                    listvariable=getRow,
                    exportselection=False,
                    selectbackground='#FFFF00',
                    selectforeground='#FF00FF')
    lb.pack(side=RIGHT, fill=BOTH, expand=False)
    lb.insert(END, ' ' * 10 + "输出列表" + ' ' * 10)
    lb.select_set(0)
    # 滚动条动，列表跟着动
    sc.config(command=lb.yview)

    ######################################################## 滚动条 END #####################################################

    ######################################################## 关闭事件 #######################################################
    def on_close():
        conda_tool_Btn.config(state='normal')
        top_conda.destroy()

    top_conda.protocol("WM_DELETE_WINDOW", on_close)

    ######################################################## 关闭事件 END #####################################################

    ######################################################## 清空 #######################################################
    def clear():
        lb.delete(0, END)
        lb.delete(0, END)
        lb.delete(0, END)
        lb.insert(END, ' ' * 52 + "输出列表" + ' ' * 52)
        lb.select_set(0)

    clear_text_btn = tk.Button(top_conda,
                               text='清空',
                               width=btn_Width,
                               height=1,
                               command=lambda: MyThread(clear),
                               relief=RIDGE,
                               font=font_Button)
    clear_text_btn.place(x=530, y=row_list[2])

    ######################################################## 清空 END #####################################################
    # 获取cmd, text_index为从第几行开始是需要的元素
    def get_cmd(command, text_index=0, getValue=False):
        with os.popen(command, 'r') as f:
            text = f.read()
        s = text.split("\n")
        result = [x for x in s if x != '']
        result_ = [x for x in s if x != ''][text_index:]
        global allResult
        allResult = []
        for row in result_:
            col = []
            str_list = row.split(' ')
            for str in str_list:
                if str != '':
                    col.append(str)
            allResult.append(col)
        clear()
        if getValue == True:
            return result[text_index:]
        else:
            for row in result:
                lb.insert(END, row)
            # close_btn(False)

    ######################################################## conda_env #######################################################
    # conda_env_list标签
    conda_env_label = tk.Label(top_conda, width=10, text='conda环境', font=font_Label)
    conda_env_label.place(x=170, y=row_list[0])

    # 初始化Combobox环境列表
    xVariable = tk.StringVar()  # #创建变量，便于取值
    env_com = ttk.Combobox(top_conda, textvariable=xVariable)  # #创建下拉菜单
    env_com.place(x=280, y=row_list[0] + 1)  # #将下拉菜单绑定到窗体

    def init_env_com():
        # 获取conda_env_list
        conda_env_list = get_cmd('conda env list', main_index_dict['conda env list'], getValue=True)
        # conda_env_list = get_cmd('ping', main_index_dict['conda env list'], getValue=True)
        env_list = []
        for value in conda_env_list:
            head_value = value.split(' ')[0]
            env_list.append(head_value)
        global env_all_list
        env_all_list = env_list
        env_com["value"] = tuple(env_list)
        env_com.current(0)  # #设定下拉菜单的默认值为第0个

    def xFunc(event):
        global current_env
        current_env = env_com.get()
        cmd = 'conda activate ' + env_com.get() + ' && conda list'
        get_cmd(cmd, main_index_dict['conda list'])
        # print(xVariable.get())  # #获取选中的值方法2

    env_com.bind("<<ComboboxSelected>>", xFunc)  # #给下拉菜单绑定事件
    # 显示环境Combobox
    init_env_com()
    ######################################################## conda_env END #####################################################

    ######################################################## 刷新 #######################################################
    conda_list_btn = tk.Button(top_conda,
                               text='刷新',
                               width=btn_Width - 6,
                               height=1,
                               command=lambda: MyThread(
                                   lambda: (init_env_com(), top_conda.update())),
                               relief=RIDGE,
                               font=font_Button)
    conda_list_btn.place(x=500, y=row_list[0] - 6)
    ######################################################## 刷新 END #####################################################

    ######################################################## 创建环境 #######################################################
    l = tk.Label(top_conda, width=10, text='创建环境', font=font_Label)
    l.place(x=9, y=row_list[5] - 3)
    # 克隆全局变量
    clone_flag = False

    def print_selection():
        global clone_flag
        if var.get() == 1:
            clone_flag = True
        else:
            clone_flag = False

    var = tk.IntVar()
    clone_env_cbtn = tk.Checkbutton(top_conda, text='克隆当前环境', variable=var, onvalue=1, offvalue=0,
                                    command=print_selection)
    clone_env_cbtn.place(x=105, y=row_list[6])
    var.set(0)

    env_name = tk.StringVar()
    env_name_Entry = tk.Entry(top_conda,
                              textvariable=env_name,
                              width=60)
    env_name_Entry.place(x=110, y=row_list[5])
    init_create_env_name = '输入环境名'
    env_name.set(init_create_env_name)

    # 创建环境
    def create_env():
        # conda create -n env_name --clone current_env
        # 拼接命令
        create_env_cmd = 'conda create -y -n '
        # 获取创建环境名env_name.get()
        if not env_name.get().startswith('输') and env_name.get().strip() != '':
            create_env_cmd += env_name.get()
            # 获取是否克隆变量clone_flag
            if clone_flag == True:
                # 获取当前环境env_com.get()
                create_env_cmd += ' --clone ' + env_com.get()
        else:
            tkm.showinfo(title='提示',
                         message='请填写要创建的环境名！')
        # 检测环境名是否存在！
        if env_name.get() not in env_all_list:
            if create_env_cmd != 'conda create -n ' and tkm.askokcancel(title='提示',
                                                                        message='是否确认创建环境[' + env_name.get() + ']？'):
                tkm.showinfo(title='提示',
                             message='正在创建环境[' + env_name.get() + ']！')
                get_cmd(create_env_cmd)
                # 提示创建完成
                tkm.showinfo(title='提示',
                             message='[' + env_name.get() + ']环境创建成功！')
                # 创建环境后重新获取conda环境
                init_env_com()
                # 强制界面显示
                top_conda.update()
                env_com.current(0)
        else:
            tkm.showinfo(title='提示',
                         message='环境[' + env_name.get() + ']已存在！')

    create_env_Btn = tk.Button(top_conda,
                               text="创建环境",
                               command=lambda: MyThread(create_env),
                               relief=RIDGE,
                               font=font_Button)
    create_env_Btn.place(x=305, y=row_list[6])

    ######################################################## 创建环境 END #####################################################

    ######################################################## 删除环境 #######################################################
    def delete_env():
        # conda remove -n 环境名 --all
        delete_env_cmd = 'conda remove -y -n '
        if tkm.askokcancel(title='提示',
                           message='是否删除当前环境[' + env_com.get() + ']？'):
            # 获取当前环境
            delete_env_cmd += env_com.get()
            # 完整命令
            delete_env_cmd += ' --all'
            get_cmd(delete_env_cmd)
            # 提示删除完成
            tkm.showinfo(title='提示',
                         message='[' + env_com.get() + ']环境删除成功！')
            # 删除环境后重新获取conda环境
            init_env_com()
            # 强制界面显示
            top_conda.update()
        else:
            tkm.showinfo(title='提示',
                         message='用户取消！')

    delete_env_Btn = tk.Button(top_conda,
                               text="  删除 环境  ",
                               command=delete_env,
                               relief=RIDGE,
                               font=font_Button)
    delete_env_Btn.place(x=290, y=row_list[1])
    ######################################################## 删除环境 END #####################################################

    ######################################################## pip list #######################################################
    pip_list_btn = tk.Button(top_conda,
                             text='pip 列表',
                             width=btn_Width,
                             height=1,
                             command=lambda: MyThread(lambda: get_cmd('pip list', main_index_dict['pip list'])),
                             relief=RIDGE,
                             font=font_Button)
    pip_list_btn.place(x=290, y=row_list[2])
    ######################################################## pip list END #####################################################

    ######################################################## conda list #######################################################
    conda_list_btn = tk.Button(top_conda,
                               text='conda 列表',
                               width=btn_Width,
                               height=1,
                               command=lambda: MyThread(
                                   lambda: get_cmd('conda activate ' + env_com.get() + ' && conda list',
                                                   main_index_dict['conda list'])),
                               relief=RIDGE,
                               font=font_Button)
    conda_list_btn.place(x=40, y=row_list[1])

    ######################################################## conda list END #####################################################

    # 获取行值
    def get_value(colIndex=0):
        return lb.get(lb.curselection()).split(' ')[colIndex]

    ######################################################## conda环境 #######################################################
    conda_env_btn = tk.Button(top_conda,
                              text='conda 环境',
                              width=btn_Width,
                              height=1,
                              command=lambda: MyThread(
                                  lambda: get_cmd('conda env list', main_index_dict['conda env list'])),
                              relief=RIDGE,
                              font=font_Button)
    conda_env_btn.place(x=530, y=row_list[1])

    ######################################################## conda环境 END #####################################################

    ######################################################## 卸载库 #######################################################
    def uninstall():
        # allResult是每个列表主要信息组成的二维数组且不含空格
        # print(allResult)
        col_len = len(allResult[0])
        # command命令字符串
        uni_str = ''
        if len(allResult) > 10:
            if col_len == 3:
                # 判断conda list中pip列表
                for index in range(len(allResult)):
                    if get_value() == allResult[index][0]:
                        if allResult[index][2].startswith('<pip>'):
                            uni_str = "pip uninstall -y " + get_value()
                        else:
                            uni_str = "conda uninstall -y " + get_value()
            else:
                uni_str = "pip uninstall -y " + get_value()

            if tkm.askokcancel(title='提示',
                               message='是否卸载' + get_value()):
                get_cmd(uni_str, 2)
            else:
                tkm.showinfo(title='提示',
                             message='用户取消！')
        else:
            tkm.showinfo(title='提示',
                         message='请选择正确的库！')

    uninstall_btn = tk.Button(top_conda,
                              text='卸载',
                              width=btn_Width,
                              height=1,
                              command=lambda: MyThread(uninstall),
                              relief=RIDGE,
                              font=font_Button)
    uninstall_btn.place(x=40, y=row_list[2])
    ######################################################## 卸载库 END #####################################################

    ######################################################## 安装库 #######################################################
    l = tk.Label(top_conda, width=10, text='pip安装', font=font_Label)
    l.place(x=10, y=row_list[3] - 3)

    pip_name = tk.StringVar()
    # pip包选择框
    pip_install_Entry = tk.Entry(top_conda,
                                 textvariable=pip_name,
                                 width=60)
    pip_install_Entry.place(x=110, y=row_list[3])
    # 输入框初始显示值
    init_showinfo = " 输入要安装的库名[可拖拽到此处] "
    pip_name.set(init_showinfo)

    def drag_pip(event):
        # 判断拖入是否为单文件
        if len(event.data.split(' ')) == 1:
            pip_name.set(event.data)
        else:
            tkm.showinfo(title='提示',
                         message='请拖入单个文件！')

    # pip 选择本地安装
    def pip_select():
        pip_path = tkf.askopenfilename(filetypes=[("pip包", ['*.whl'])])
        if pip_path != "":
            pip_name.set(pip_path)
        else:
            tkm.showinfo(title='提示',
                         message='用户取消！')

    # pip安装
    def pip_install():
        if pip_name.get() != init_showinfo and pip_name.get().strip() != '':
            new_cmd = pip_name.get()
            if tkm.askokcancel(title='提示',
                               message='是否确认安装' + new_cmd + '到' + env_com.get() + '环境下？'):
                tkm.showinfo(title='提示',
                             message='安装中！')
                if env_com.get() == 'base':
                    pip_install_cmd = 'conda activate ' + env_com.get() + \
                                      ' && pip install ' + pip_name.get() + \
                                      ' -i  https://pypi.douban.com/simple'
                else:
                    pip_install_cmd = 'conda activate ' + env_com.get() + \
                                      ' && conda install -y ' + pip_name.get()
                # 发送命令
                get_cmd(pip_install_cmd)
        elif pip_name.get().strip() == '':
            tkm.showinfo(title='提示',
                         message='请输入包名！')
        else:
            tkm.showinfo(title='提示',
                         message='请先输入包名或选择文件包再安装！')

    # 实现拖拽
    pip_install_Entry.drop_target_register(DND_FILES)
    pip_install_Entry.dnd_bind('<<Drop>>', drag_pip)
    # 库选择按钮
    pip_select_Btn = tk.Button(top_conda,
                               text="选择",
                               command=pip_select,
                               relief=RIDGE,
                               font=font_Button)
    pip_select_Btn.place(x=270, y=row_list[4])
    # 库安装按钮
    pip_install_Btn = tk.Button(top_conda,
                                text="安装",
                                command=lambda: MyThread(pip_install),
                                relief=RIDGE,
                                font=font_Button)
    pip_install_Btn.place(x=400, y=row_list[4])
    ######################################################## 安装库 END #####################################################

    ######################################################## 超链接 #######################################################
    # 网址1
    link_btn1 = tk.Button(top_conda,
                          text='清华源',
                          width=btn_Width,
                          height=1,
                          command=lambda:
                          MyThread(lambda: webbrowser.open_new
                          (r"https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/")),
                          relief=RIDGE,
                          font=font_Button)
    link_btn1.place(x=40, y=row_list[7])

    # 网址2
    link_btn2 = tk.Button(top_conda,
                          text='Python包',
                          width=btn_Width,
                          height=1,
                          command=lambda:
                          MyThread(lambda: webbrowser.open_new
                          (r"https://www.lfd.uci.edu/~gohlke/pythonlibs/")),
                          relief=RIDGE,
                          font=font_Button)
    link_btn2.place(x=290, y=row_list[7])

    # 网址3
    link_btn3 = tk.Button(top_conda,
                          text='Pytorch包',
                          width=btn_Width,
                          height=1,
                          command=lambda:
                          MyThread(lambda: webbrowser.open_new
                          (r"https://download.pytorch.org/whl/torch_stable.html")),
                          relief=RIDGE,
                          font=font_Button)
    link_btn3.place(x=530, y=row_list[7])

    # 网址4
    link_btn4 = tk.Button(top_conda,
                          text='Pytorch文档',
                          width=btn_Width,
                          height=1,
                          command=lambda:
                          MyThread(lambda: webbrowser.open_new
                          (r"https://pytorch.org/docs/master/index.html")),
                          relief=RIDGE,
                          font=font_Button)
    link_btn4.place(x=40, y=row_list[8])

    # 网址5
    link_btn5 = tk.Button(top_conda,
                          text='OpenCV文档',
                          width=btn_Width,
                          height=1,
                          command=lambda:
                          MyThread(lambda: webbrowser.open_new
                          (r"http://woshicver.com/")),
                          relief=RIDGE,
                          font=font_Button)
    link_btn5.place(x=290, y=row_list[8])

    # 网址6
    link_btn6 = tk.Button(top_conda,
                          text='Pytorch官网',
                          width=btn_Width,
                          height=1,
                          command=lambda:
                          MyThread(lambda: webbrowser.open_new
                          (r"https://pytorch.org/get-started/locally/")),
                          relief=RIDGE,
                          font=font_Button)
    link_btn6.place(x=530, y=row_list[8])
    ######################################################## 超链接 END #####################################################


conda_tool_img = PhotoImage(file=conda_tool_image)
conda_tool_Btn = tk.Button(top,
                           text='Conda工具',
                           command=lambda: MyThread(conda_tool),
                           relief=RIDGE,
                           image=conda_tool_img,
                           bd=0,
                           # width=9,
                           # height=2,
                           font=font_Button)
conda_tool_Btn.place(x=button_column_x[6], y=button_right_y[2])
ToolTip(conda_tool_Btn, msg=conda_tool_Btn['text'] + "(安装/卸载pip和conda包、创建/删除conda环境) ", msgFunc=None, follow=True,
        delay=0)
################################################### Conda工具 End #################################################


################################################### 关于 ####################################################
about_img = PhotoImage(file=about_image)
about_btn = tk.Button(top,
                      text='关于',
                      command=lambda:
                      tkm.showinfo(title='关于',
                                   message='       软件开发\n    西安石油大学    \n           罗凯'),
                      relief=RIDGE,
                      image=about_img,
                      bd=0,
                      # width=9,
                      # height=2,
                      font=font_Button)
about_btn.place(x=button_column_x[8], y=button_right_y[2])
ToolTip(about_btn, msg=about_btn['text'], msgFunc=None, follow=True, delay=0)


################################################### 关于 End #################################################


################################################### 本地检测 ####################################################
def local_detect():
    global num_runs  # 记录选择的文件个数
    global reader

    num_runs = 1
    params = get_params(useCamera=True)
    params.append('-dr')
    params.append('open')
    try:
        close_radio(True)
        start_detect_Btn.config(state='disable')
        stop_detect_Btn.config(state='normal')
        reader = ProcessOutputReader(line_queue, 'python', params=params)
        reader.start()
        close_radio(False)
    except AssertionError:
        pass
    close_radio(True)
    start_detect_Btn.config(state='disable')
    stop_detect_Btn.config(state='normal')


local_detect_Btn = tk.Button(top,
                             text='基于yolov3-tiny的口罩佩戴检测程序',
                             command=lambda: MyThread(local_detect),
                             relief=RIDGE,
                             width=32,
                             height=1,
                             bg='yellow',
                             bd=0,
                             font=font_Title,
                             )
local_detect_Btn.place(x=520, y=35, anchor='nw')
ToolTip(local_detect_Btn, msg='使用分离窗口实时检测', msgFunc=None, follow=True, delay=0)
################################################### 本地检测 End #################################################


################################################### 置顶和取消置顶 ####################################################
placed_top_img = PhotoImage(file=placed_top_image)
cancel_top_img = PhotoImage(file=cancel_top_image)
click_top_state = False


def placed_top_func():
    global click_top_state
    if click_top_state:
        click_top_state = False
        top.wm_attributes('-topmost', 0)
        placed_top_button.config(image=placed_top_img)
    else:
        click_top_state = True
        top.wm_attributes('-topmost', 1)
        placed_top_button.config(image=cancel_top_img)


placed_top_button = tk.Button(top,
                              text='窗口置顶',
                              command=placed_top_func,
                              relief=FLAT,
                              image=placed_top_img,
                              bd=0,
                              font=font_Button)
placed_top_button.place(x=5, y=5)
ToolTip(placed_top_button, msg=placed_top_button['text'] + "/取消置顶", msgFunc=None, follow=True, delay=0)
################################################### 置顶和取消置顶 End #################################################


################################################### 透明度 ####################################################
# 透明度标签
transparent_label = tk.Label(top,
                             width=7,
                             text='窗口透明度',
                             font=font_Scale)
transparent_label.place(x=1500, y=840)
# 透明度显示
continue_slide_flag = False


def transparent_selection(value):
    global continue_slide_flag
    global transparent
    transparent = value
    if float(value) <= 0.4 and not continue_slide_flag:
        Thread(target=tkm.showinfo, args=('提示', '继续滑动可能造成窗口消失！',)).start()
        continue_slide_flag = True
    else:
        top.attributes('-alpha', transparent)


# 透明度Scale
transparent_scale = tk.Scale(top,
                             from_=0,
                             to=1,
                             orient=HORIZONTAL,
                             length=220,
                             showvalue=0,
                             tickinterval=0.2,
                             resolution=0.01,
                             command=transparent_selection)
transparent_scale.place(x=1435, y=860)
transparent_scale.set(1)
################################################### 透明度 End #################################################

cv2.destroyAllWindows()
top.mainloop()
